package com.hailiao.compress.demo.page

import android.annotation.SuppressLint
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import com.d10ng.pcmresample.DLPcmResampleUtil
import com.d10ng.pcmresample.constant.ChannelType
import com.d10ng.pcmresample.constant.EncodingType
import com.d10ng.voice.createPCMVoicePlayer
import com.d10ng.voice.createPCMVoiceRecorder
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.hailiao.util.speechio.SpeechCompressLevel
import com.hailiao.util.speechio.SpeechCompressPlatform
import com.hailiao.util.speechio.denoise
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun SpeechPage(
    modifier: Modifier = Modifier,
    model: SpeechPageViewModel = viewModel()
) {
    val audioPermissionState = rememberPermissionState(
        android.Manifest.permission.RECORD_AUDIO
    )

    val level by model.levelFlow.collectAsState()
    val recording by model.recordingFlow.collectAsState()
    val recordTimeText by model.recordTimeTextFlow.collectAsState("")
    val recordVolumeList by model.recordVolumeListFlow.collectAsState()
    val pcmDataSize by model.pcmDataSizeFlow.collectAsState()
    val encodeDataSize by model.encodeDataSizeFlow.collectAsState()
    val encoding by model.encodingFlow.collectAsState()
    val noiseReduction by model.noiseReductionFlow.collectAsState()
    val playing by model.playingFlow.collectAsState()
    val playTimeText by model.playTimeTextFlow.collectAsState("")
    val playVolumeList by model.playVolumeListFlow.collectAsState()

    var expanded by remember { mutableStateOf(false) }

    if (audioPermissionState.status.isGranted) {
        Column(modifier.verticalScroll(rememberScrollState())) {
            Button(
                onClick = {
                    if (recording) model.stopRecord() else model.startRecord()
                },
                enabled = !encoding && !playing
            ) {
                Text(if (recording) "停止录音" else "开始录音")
            }
            VolumeListBar(list = recordVolumeList)
            Text("当前录音时长: $recordTimeText")
            Text("录音源文件PCM数据大小: $pcmDataSize")
            Spacer(modifier = Modifier.height(8.dp))
            // 压缩等级选择器控件
            Box {
                TextField(
                    value = level?.name ?: "不压缩",
                    enabled = false,
                    onValueChange = {},
                    label = { Text("压缩等级，数字越大压缩率越高") },
                    singleLine = true,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 8.dp)
                        .clickable { expanded = !expanded }
                )
                DropdownMenu(
                    expanded = expanded,
                    onDismissRequest = { expanded = false }
                ) {
                    SpeechCompressLevel.entries.plus(null).forEach {
                        DropdownMenuItem(
                            text = { Text(it?.name?: "不压缩") },
                            onClick = {
                                model.setLevel(it)
                                expanded = false
                            }
                        )
                    }
                }
            }
            // 是否开启降噪切换控件
            Row(
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text("是否开启降噪")
                Spacer(modifier = Modifier.width(16.dp))
                Switch(
                    checked = noiseReduction,
                    onCheckedChange = { checked ->
                        model.setNoiseReduction(checked)
                    }
                )
            }
            // 压缩按钮
            Button(
                onClick = {
                    model.onClickEncode()
                },
                enabled = !recording && !encoding && !playing && pcmDataSize != 0
            ) {
                Text(if (encoding) "压缩中..." else "压缩")
            }
            Text("压缩后文件数据大小: $encodeDataSize")
            Text(if (encoding) "正在压缩中...，不可点击其他按钮" else "压缩完成")
            Spacer(modifier = Modifier.height(8.dp))
            Button(
                onClick = {
                    if (playing) model.stopPlay() else model.startPlay()
                },
                enabled = !recording && !encoding && encodeDataSize != 0
            ) {
                Text(if (playing) "停止播放" else "播放解压后录音文件")
            }
            VolumeListBar(list = playVolumeList)
            Text("当前播放状态: $playing")
            Text("当前播放时长: $playTimeText")
        }
    } else {
        Box(
            modifier = modifier,
            contentAlignment = Alignment.Center
        ) {
            Button(onClick = { audioPermissionState.launchPermissionRequest() }) {
                Text("请求录音权限")
            }
        }
    }
}

@Composable
fun VolumeListBar(
    list: List<Float>
) {
    Row(
        modifier = Modifier
            .height(50.dp)
            .fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically
    ) {
        list.forEach {
            Box(
                modifier = Modifier
                    .padding(start = 2.dp)
                    .fillMaxHeight()
                    .width(4.dp)
                    .background(Color.LightGray),
                contentAlignment = Alignment.Center
            ) {
                Box(
                    modifier = Modifier
                        .fillMaxHeight(it)
                        .width(4.dp)
                        .background(Color.Green)
                )
            }
        }
    }
}

class SpeechPageViewModel: ViewModel() {
    // 音频录制采样率
    private val recordSampleHz = 48000
    // 音频录制工具
    private val recorder = createPCMVoiceRecorder(sampleRate = recordSampleHz)
    // 音频播放采样率
    private val playSampleHz = 8000
    // 音频播放工具
    private val player = createPCMVoicePlayer()

    // 压缩等级
    val levelFlow = MutableStateFlow<SpeechCompressLevel?>(SpeechCompressLevel.L10)
    // 录音状态
    val recordingFlow = recorder.getRecordStatusFlow()
    // 录音时长
    val recordTimeTextFlow = recorder.getRecordTimeTextFlow()
    // 录音音量
    val recordVolumeListFlow = MutableStateFlow(List(40) { 0f })
    // 录音文件大小
    val pcmDataSizeFlow = MutableStateFlow(0)
    // 压缩后文件大小
    val encodeDataSizeFlow = MutableStateFlow(0)
    // 压缩状态
    val encodingFlow = MutableStateFlow(false)
    // 降噪是否开启
    val noiseReductionFlow = MutableStateFlow(true)

    // 原始录音数据
    private var pcmData: ByteArray = byteArrayOf()
    // 压缩后数据
    private var encodeData: ByteArray = byteArrayOf()
    // 解压后数据
    private var decodeData: ByteArray = byteArrayOf()

    // 播放状态
    val playingFlow = player.getPlayStatusFlow()
    // 播放时长
    val playTimeTextFlow = player.getPlayTimeTextFlow()
    // 播放音量
    val playVolumeListFlow = MutableStateFlow(List(40) { 0f })

    init {
        viewModelScope.launch(Dispatchers.IO) {
            launch {
                recorder.getRecordVolumeUpdateEvent().collect {
                    val ls = recordVolumeListFlow.value.toMutableList()
                    ls.add(it)
                    ls.removeAt(0)
                    recordVolumeListFlow.value = ls
                }
            }
            launch {
                player.getPlayVolumeUpdateEvent().collect {
                    val ls = playVolumeListFlow.value.toMutableList()
                    ls.add(it)
                    ls.removeAt(0)
                    playVolumeListFlow.value = ls
                }
            }
        }
    }

    /**
     * 修改等级
     */
    fun setLevel(level: SpeechCompressLevel?) {
        levelFlow.value = level
    }

    /**
     * 设置降噪
     */
    fun setNoiseReduction(value: Boolean) {
        noiseReductionFlow.value = value
    }

    /**
     * 压缩
     */
    fun onClickEncode() {
        if (encodingFlow.value) return
        encodingFlow.value = true
        val level = levelFlow.value
        viewModelScope.launch(Dispatchers.IO) {
            if (level == null) {
                // 无须压缩
                // 判断是否需要降噪
                var data = if (noiseReductionFlow.value) denoise(pcmData) else pcmData
                // 需要将48KHz音频压缩到8KHz
                data = DLPcmResampleUtil.resample(
                    data,
                    48000,
                    8000,
                    ChannelType.MONO,
                    EncodingType.PCM_16BIT
                )
                encodingFlow.value = false
                encodeData = data
                decodeData = data
                encodeDataSizeFlow.value = encodeData.size
                return@launch
            }
            // 压缩
            encodeData = SpeechCompressPlatform.encode(level, pcmData, noiseReductionFlow.value)
            // 解压
            decodeData = SpeechCompressPlatform.decode(level, encodeData)
            encodeDataSizeFlow.value = encodeData.size
            encodingFlow.value = false
        }
    }

    @SuppressLint("MissingPermission")
    fun startRecord() {
        recorder.start()
    }

    fun stopRecord() {
        pcmData = recorder.stop().copyOf()
        pcmDataSizeFlow.value = pcmData.size
    }

    fun startPlay() {
        if (decodeData.isEmpty()) return
        player.start(decodeData, playSampleHz)
    }

    fun stopPlay() {
        player.stop()
    }
}